import { assert, test } from "@marcisbee/rion/test";
import { createRoot, update } from "../client.ts";

test.before.each(() => {
  document.body.innerHTML = "";
});

/**
 * Component whose displayed count increments on each update event.
 */
function Counting(this: HTMLElement) {
  let count = 0;
  this.addEventListener("update", () => {
    count++;
  });
  return () => <div className="count">{() => count}</div>;
}

/**
 * Component with nested reactive child (function child) plus outer counter.
 * Outer counter increments on host update; inner span content is regenerated by nested reactive render.
 */
function ReactiveParent(this: HTMLElement) {
  let outer = 0;
  this.addEventListener("update", () => {
    outer++;
  });

  return () => (
    <div className="parent">
      <span className="outer">{() => outer}</span>
      {() => (
        <span className="inner">{Math.random().toString().slice(0, 8)}</span>
      )}
    </div>
  );
}

/**
 * update-increments
 * root.update() dispatches update events, causing reactive host to re-render.
 */
test("update-increments", () => {
  const root = createRoot(document.body);
  root.render(<Counting />);

  const getText = () =>
    (document.querySelector(".count") as HTMLDivElement).textContent;

  assert.equal(getText(), "0");
  update(root.root!);
  assert.equal(getText(), "1");
  update(root.root!);
  assert.equal(getText(), "2");
});

/**
 * update-no-remount
 * root.update() does not trigger additional connect/disconnect events (no remount).
 */
test("update-no-remount", () => {
  const instance = <Counting />;
  let connects = 0;
  let disconnects = 0;
  instance.addEventListener("connect", () => connects++);
  instance.addEventListener("disconnect", () => disconnects++);

  const root = createRoot(document.body);
  root.render(instance);
  assert.equal(connects, 1);
  assert.equal(disconnects, 0);

  update(root.root!);
  update(root.root!);
  update(root.root!);
  assert.equal(connects, 1);
  assert.equal(disconnects, 0);
});

/**
 * update-nested-reactive
 * root.update() propagates update to nested reactive region (function child) & outer host.
 */
test("update-nested-reactive", () => {
  const root = createRoot(document.body);
  root.render(<ReactiveParent />);

  const outerEl = () =>
    (document.querySelector(".outer") as HTMLSpanElement).textContent;
  const innerEl = () =>
    (document.querySelector(".inner") as HTMLSpanElement).textContent;

  const firstOuter = outerEl();
  const firstInner = innerEl();
  assert.equal(firstOuter, "0");
  assert.true(firstInner.length > 0);

  update(root.root!);
  const secondOuter = outerEl();
  const secondInner = innerEl();
  assert.equal(secondOuter, "1");
  assert.notEqual(secondInner, firstInner);

  update(root.root!);
  const thirdOuter = outerEl();
  const thirdInner = innerEl();
  assert.equal(thirdOuter, "2");
  assert.notEqual(thirdInner, secondInner);
});

await test.run();
